#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <regex.h>

#include "DemoDateTime.h"
#include "DemoString.h"

void stringJoin(void) {
    
    char *part1 = "七律·长征";
    char *part2 = "毛泽东";
    char *part3 = "1935年10月";
    char *part4 = "红军不怕远征难，万水千山只等闲。";
    char *part5 = "五岭逶迤腾细浪，乌蒙磅礴走泥丸。";
    char *part6 = "金沙水拍云崖暖，大渡桥横铁索寒。";
    char *part7 = "更喜岷山千里雪，三军过后尽开颜。";
    
    char all[1024] = {0};
    strcat(all, part1);
    strcat(all, part2);
    strcat(all, part3);
    strcat(all, part4);
    strcat(all, part5);
    strcat(all, part6);
    strcat(all, part7);
    printf ("stringJoin by strcat, %s\n", all);

    sprintf(all, "%s\n%s\n%s\n%s\n%s\n%s\n%s", part1, part2, part3, part4, part5, part6, part7);
    printf ("stringJoin by sprintf, %s\n", all);
}

char** compentArrayFromString2(char* inputStr, char* sepStr, int* compentCount) {
    int sepLen = strlen(sepStr);
    if (0 == sepLen || 0 == strlen(inputStr)) {
        *compentCount = 0;
        return NULL;
    }

    char* copyStr = malloc(strlen(inputStr)+1);
    strcpy(copyStr, inputStr);

    char *strPtr = copyStr;
    char *sepPtr = strstr(strPtr, sepStr);
    int componentNum = 0;
    
    // 子字符串数量 不会超过 字符串长度
    char ** componentStrArrMax = (char **)malloc(sizeof(char*) * strlen(inputStr));
    
    while (sepPtr != NULL) {
        // sepPtr 指针指向的字符赋值为0，如此，strPtr就是一个 子字符串
        *sepPtr = 0;
        componentStrArrMax[componentNum] = strPtr;
        componentNum += 1;

        // 找下一个 子字符串
        strPtr = sepPtr;
        strPtr += sepLen;
        sepPtr = strstr(strPtr, sepStr);
    }

    componentStrArrMax[componentNum] = strPtr;
    componentNum += 1;
    
    // 拷贝有效值，返回
    *compentCount = componentNum;
    char ** componentStrArr = (char **)malloc(sizeof(char*) * componentNum);
    for (int i=0; i<componentNum; ++i) {
        componentStrArr[i] = componentStrArrMax[i];
    }
    
    // componentStrArrMax 无用，释放之
    free(componentStrArrMax);
    return componentStrArr;
}

char** compentArrayFromString(char* inputStr, char* sepStr, int* compentCount) {
    int sepLen = strlen(sepStr);
    if (0 == sepLen || 0 == strlen(inputStr)) {
        *compentCount = 0;
        return NULL;
    }

    char *strPtr = inputStr;

    // 先确定分割出来的字符串数量 componentNum
    // char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c（一个无符号字符）的位置。如果未找到该字符则返回 NULL。
    // char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置，不包含终止符 '\0'。
    char *sepPtr = strstr(strPtr, sepStr);
    int componentNum = 1;
    while (sepPtr != NULL) {
        componentNum += 1;

        strPtr = sepPtr;
        strPtr += sepLen;
        sepPtr = strstr(strPtr, sepStr);
    }
    //printf ("componentNum, %d\n", componentNum);

    // 申请存储 componentNum 个字符串指针的空间。
    char ** componentStrArr = (char **)malloc(sizeof(char*) * componentNum);

    // 处理每分割出来一个 子字符串，
    strPtr = inputStr;
    sepPtr = strstr(strPtr, sepStr);//strchr(strPtr, '-');
    componentNum = 0;
    while (sepPtr != NULL) {
        // 申请存储空间，做内存拷贝，别忘记c字符串末尾0。
        componentStrArr[componentNum] = (char*)malloc(sepPtr - strPtr +1);
        memcpy(componentStrArr[componentNum], strPtr, sepPtr - strPtr);
        componentStrArr[componentNum][sepPtr - strPtr] = 0;
        componentNum += 1;
        //printf("strPtr %s, sepPtr %s, componentStrArr[%d] = %s\n", strPtr, sepPtr, componentNum, componentStrArr[componentNum]);

        strPtr = sepPtr;
        strPtr += sepLen;
        sepPtr = strstr(strPtr, sepStr);//strchr(strPtr, '-');
        
    }

    componentStrArr[componentNum] = (char*)malloc(strlen(strPtr)+1);
    strcpy(componentStrArr[componentNum], strPtr);
    componentNum += 1;
    //printf("strPtr %s, sepPtr %s, componentStrArr[%d] = %s\n", strPtr, sepPtr, componentNum, componentStrArr[componentNum]);
    
    *compentCount = componentNum;
    return componentStrArr;
}

void stringFromToArray(void) {
 
    char *monthArr[] = {
                       "January",
                       "February",
                       "March",
                       "April",
                       "May",
                       "June",
                       "July",
                       "August",
                       "September",
                       "October",
                       "November",
                       "December"
                       };
    printf("sizeof monthArr=%ld\n", sizeof(monthArr));
    // 数组 --'分隔符'--> 字符串
    char monthsStr[1024] = {0};
    for (int i=0;i<12;++i){
        if (strlen(monthsStr) > 0) {
            strcat(monthsStr, "|");
        }
        strcat(monthsStr, monthArr[i]);   
    }
    printf ("Array -> string, %s\n", monthsStr);

    // 字符串 --'分隔符'--> 数组
    char *weekdayStr = "Mon--Tue--Wed--Thu--Fri--Sat--Sun";
    int componentNum = 0;
    char** compentArray = compentArrayFromString(weekdayStr, "--", &componentNum);
    
    // 遍历check
    for (int i=0;i<componentNum;++i) {
        printf("compentArray[%d] = %s\n", i, compentArray[i]);
    }
    // 遍历 内存回收
    for (int i=0;i<componentNum;++i) {
        free(compentArray[i]);
    }
    free(compentArray);


    int componentNum2 = 0;
    char** compentArray2 = compentArrayFromString2(weekdayStr, "--", &componentNum2);
    // 遍历check
    for (int i=0;i<componentNum2;++i) {
        printf("compentArray2[%d] = %s\n", i, compentArray2[i]);
    }
    // 遍历 内存回收
    //for (int i=0;i<componentNum2;++i) {
        free(compentArray2[0]);
    //}
    free(compentArray2);
 }

// 从末尾向开头 查找ostr 第一处匹配 fstr 的
char* findSubStrBackward(char* ostr, char*fstr) {
    if (0 == strlen(fstr) || 0 == strlen(ostr) || strlen(ostr) < strlen(fstr)) {
        return NULL;
    }
    
    char* strBackward = ostr;
    strBackward += strlen(ostr) - strlen(fstr);
    
    char *subStr = strstr(strBackward, fstr);
    if (subStr) {
        // strBackward 还没有向字符串开头移动就找到了
        return subStr;
    }

    do {
        // strBackward 向字符串开头移动 然后做子串查找
        strBackward -= 1;
        subStr = strstr(strBackward, fstr);
        if (subStr) {
            return subStr;
        }
    } while (strBackward != ostr);

    return NULL;
}

char* replaceSubStr(char* ostr, char* fstr, char* replaceStr) {
    if (0 == strlen(ostr) || 0 == strlen(fstr) || 0 == strlen(replaceStr)) {
        char *strCopy = (char *)malloc(strlen(ostr)+1);
        strcpy(strCopy, ostr);
        return strCopy;
    }
    
    char *strRtn = NULL;
    if (strlen(replaceStr) <= strlen(fstr)) {
        strRtn = (char *)malloc(strlen(ostr)+1);
    } else {
        // 按最大匹配 申请 返回字符串 存储空间
        long strRtnLen = 1 + (long)(ceilf(strlen(ostr) * (float)(strlen(replaceStr)) / (float)(strlen(fstr))));
        strRtn = (char *)malloc(strRtnLen+1);
    }
    
    char *subStr = ostr;
    char *matchStr = strstr(subStr, fstr);
    char *copy2Str = strRtn;
    while (matchStr != NULL) {
        // 找到一处 匹配 fstr
        long copyCount = matchStr-subStr;
        if (copyCount > 0) {
            // 拷贝 匹配 fstr 之前的部分字符
            memcpy(copy2Str, subStr, copyCount);
            copy2Str += copyCount;
            
            // 拷贝 replaceStr, 达成 替换
            memcpy(copy2Str, replaceStr, strlen(replaceStr));
            copy2Str += strlen(replaceStr);
            
            // 移动 subStr 寻找下一处匹配
            subStr += copyCount;
            subStr += strlen(fstr);
            
            matchStr = strstr(subStr, fstr);
        }
    }
    if (strlen(subStr) > 0) {
        memcpy(copy2Str, subStr, strlen(subStr));
        copy2Str += strlen(subStr);
    }
    
    // 添加字符串结束 0
    *copy2Str = 0;
    
    return strRtn;
}

void subStringSearch(void) {
 
    char *ostr = "浔阳江头夜送客，枫叶荻花秋瑟瑟。主人下马客在船，举酒欲饮无管弦。醉不成欢惨将别，别时茫茫江浸月。忽闻水上琵琶声，主人忘归客不发。寻声暗问弹者谁，琵琶声停欲语迟。移船相近邀相见，添酒回灯重开宴。千呼万唤始出来，犹抱琵琶半遮面。转轴拨弦三两声，未成曲调先有情。弦弦掩抑声声思，似诉平生不得志。低眉信手续续弹，说尽心中无限事。轻拢慢捻抹复挑，初为《霓裳》后《六幺》。大弦嘈嘈如急雨，小弦切切如私语。嘈嘈切切错杂弹，大珠小珠落玉盘。间关莺语花底滑，幽咽泉流冰下难。冰泉冷涩弦凝绝，凝绝不通声暂歇。别有幽愁暗恨生，此时无声胜有声。银瓶乍破水浆迸，铁骑突出刀枪鸣。曲终收拨当心画，四弦一声如裂帛。东船西舫悄无言，唯见江心秋月白。沉吟放拨插弦中，整顿衣裳起敛容。自言本是京城女，家在虾蟆陵下住。十三学得琵琶成，名属教坊第一部。曲罢曾教善才服，妆成每被秋娘妒。五陵年少争缠头，一曲红绡不知数。钿头银篦击节碎，血色罗裙翻酒污。今年欢笑复明年，秋月春风等闲度。弟走从军阿姨死，暮去朝来颜色故。门前冷落鞍马稀，老大嫁作商人妇。商人重利轻别离，前月浮梁买茶去。去来江口守空船，绕船月明江水寒。夜深忽梦少年事，梦啼妆泪红阑干。我闻琵琶已叹息，又闻此语重唧唧。同是天涯沦落人，相逢何必曾相识！我从去年辞帝京，谪居卧病浔阳城。浔阳地僻无音乐，终岁不闻丝竹声。住近湓江地低湿，黄芦苦竹绕宅生。其间旦暮闻何物？杜鹃啼血猿哀鸣。春江花朝秋月夜，往往取酒还独倾。岂无山歌与村笛？呕哑嘲哳难为听。今夜闻君琵琶语，如听仙乐耳暂明。莫辞更坐弹一曲，为君翻作《琵琶行》。感我此言良久立，却坐促弦弦转急。凄凄不似向前声，满座重闻皆掩泣。座中泣下谁最多？江州司马青衫湿。";
 
    // 从头到尾 查找第一处
    // char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置，不包含终止符 '\0'。
    printf("subStringSearch, utf8 源代码中, %s strlen=%ld\n", "琵琶", strlen("琵琶"));
    char *firstMatch = strstr(ostr, "琵琶");
    
    if (firstMatch) {
        printf("subStringSearch, 第一处 \"%s\", offset=%ld\n", "琵琶", firstMatch-ostr);
        char *firstSub = (char *)malloc(strlen(firstMatch)+1);
        strcpy(firstSub, firstMatch);
        printf("subStringSearch, firstSub: %s\n", firstSub);
        free(firstSub);
    } else {
        printf("subStringSearch, 找不到 \"%s\"\n", "琵琶");
    }

    
    // 从末尾向开头 查找第一处
    char *firstMatchBackward = findSubStrBackward(ostr, "琵琶");
    if (firstMatchBackward) {
        printf("subStringSearch, 倒着查找 第一处 \"%s\", offset=%ld\n", "琵琶", firstMatchBackward-ostr);
        char *firstSub = (char *)malloc(strlen(firstMatchBackward)+1);
        strcpy(firstSub, firstMatchBackward);
        printf("subStringSearch, 倒着查找 firstSub: %s\n", firstSub);
        free(firstSub);
    } else {
        printf("subStringSearch, 找不到 \"%s\"\n", "琵琶");
    }

//
//    // 替换所有命中的 琵琶 为 PiPa
    char *replaceStr = replaceSubStr(ostr, "琵琶", "(__Pi_Pa__)");
    if (replaceStr) {
        printf("subStringSearch, 替换所有命中的 琵琶 为 PiPa, ostr len=%ld, replace len=%ld, %s\n", strlen(ostr), strlen(replaceStr), replaceStr);
        free(replaceStr);
    } else {
        printf("subStringSearch, replaceSubStr return null \n");
    }
}

// malloc 申请足够空间，拷贝srcStr后返回
char* malloc2Strcpy(char* srcStr) {
    if (0 == strlen(srcStr)) {
        return NULL;
    }
    char *rtn = malloc(strlen(srcStr)+1);
    strcpy(rtn, srcStr);
    return rtn;
}

char* defaultWhiteSpaces4Trim(void) {
    char* whiteSpaces = (char *)malloc(8);
    
    // ASCII 9到13 和 32 作为默认的trim字符集合
    whiteSpaces[0] = 9;
    whiteSpaces[1] = 10;
    whiteSpaces[2] = 11;
    whiteSpaces[3] = 12;
    whiteSpaces[4] = 13;
    whiteSpaces[5] = 32;
    whiteSpaces[6] = 0;
    return whiteSpaces;
}

char* stringWhiteSpaceLeftTrimed(char *str4Trim, char *whiteSpaces) {
    size_t oStrLen = strlen(str4Trim);
    if(0 == oStrLen) {
        return NULL;
    }
    if(0 == strlen(whiteSpaces)) {
        whiteSpaces = " ";
    }
    
    char* headPtr = str4Trim;    // 指向 str4Trim 头部的指针
    size_t loop = 0;
    do {
        // 从头向尾遍历
        if(! strchr(whiteSpaces, *headPtr)) {
            // headPtr 指向的字符，在whiteSpaces中 没有被找到
            break;
        }
        headPtr += 1;
        ++ loop;
    } while (loop < oStrLen);
    
    return malloc2Strcpy(headPtr);
}
 
char* stringWhiteSpaceRightTrimed(char *str4Trim, char *whiteSpaces) {
    if(0 == strlen(str4Trim)) {
        return NULL;
    }
    if(0 == strlen(whiteSpaces)) {
        whiteSpaces = " ";
    }
    char* rtn = malloc2Strcpy(str4Trim);
    if(0 == strlen(rtn)) {
        return rtn;
    }
    
    char* tailPtr = rtn;    // 指向 rtn 尾部的指针
    tailPtr += strlen(str4Trim);
    
    do {
        // 从尾向头遍历
        tailPtr -= 1;
        if(strchr(whiteSpaces, *tailPtr)) {
            // tailPtr 指向的字符，在whiteSpaces中能被找到
            // tailPtr 指向的字符赋值为0，相当于从尾部缩减 rtn 字符;
            *tailPtr = 0;
        } else {
            break;
        }
    } while (tailPtr != rtn);
    
    return rtn;
}

char* stringWhiteSpaceTrimed(char *str4Trim, char *whiteSpaces) {
    return stringWhiteSpaceRightTrimed(stringWhiteSpaceLeftTrimed(str4Trim, whiteSpaces), whiteSpaces);
}

char* stringLeftTrimed(char *str4Trim) {
    return stringWhiteSpaceLeftTrimed(str4Trim, defaultWhiteSpaces4Trim());
}

char* stringRightTrimed(char *str4Trim) {
    return stringWhiteSpaceRightTrimed(str4Trim, defaultWhiteSpaces4Trim());
}

char* stringTrimed(char *str4Trim) {
    return stringWhiteSpaceTrimed(str4Trim, defaultWhiteSpaces4Trim());
}
 
void stringTrim(void) {
    char* ostr = " \t 2sapce+1tab 山外青山楼外楼 3space   ";
    char* trimstr = stringTrimed(ostr);
    char* leftTrimstr = stringLeftTrimed(ostr);
    char* rightTrimstr = stringRightTrimed(ostr);
 
    printf("\nstringTrim\nostr        :|%s|\ntrimstr     :|%s|\nleftTrimstr :|%s|\nrightTrimstr:|%s|\n", ostr, trimstr, leftTrimstr, rightTrimstr);
}

void stringFormat(void) {
    // http://c.biancheng.net/view/159.html
    // https://blog.csdn.net/K346K346/article/details/52252626
    /*
     类型（type）表
     %c                     输出一个单一的字符
     %s                     输出一个字符串
     %p                     以16进制形式输出指针
     %hd、%d、%ld            以十进制、有符号的形式输出 short、int、long 类型的整数
     %hu、%u、%lu            以十进制、无符号的形式输出 short、int、long 类型的整数
     %ho、%o、%lo            以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数
     %#ho、%#o、%#lo         以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数
     %hx、%x、%lx
     %hX、%X、%lX            以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写，那么输出的十六进制数字也小写；如果 X 大写，那么输出的十六进制数字也大写。
     %#hx、%#x、%#lx
     %#hX、%#X、%#lX         以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写，那么输出的十六进制数字和前缀都小写；如果 X 大写，那么输出的十六进制数字和前缀都大写。
     %f、%lf                 以十进制的形式输出 float、double 类型的小数
     %e、%le
     %E、%lE                 以指数的形式输出 float、double 类型的小数。如果 e 小写，那么输出结果中的 e 也小写；如果 E 大写，那么输出结果中的 E 也大写。
     %g、%lg
     %G、%lG                 以十进制和指数中较短的形式输出 float、double 类型的小数，并且小数部分的最后不会添加多余的 0。如果 g 小写，那么当以指数形式输出时 e 也小写；如果 G 大写，那么当以指数形式输出时 E 也大写。
     
     标志（flags）表
     -      减号     结果左对齐，右边填空格。默认是右对齐，左边填空格。
     +      加号     输出符号(正号或负号)
     space  空格     输出值为正时加上空格，为负时加上负号
     #      井号     type是o、x、X时，增加前缀0、0x、0X。
                    type是a、A、e、E、f、g、G时，一定使用小数点。默认的，如果使用.0控制不输出小数部分，则不输出小数点。
                    type是g、G时，尾部的0保留。
     0    数字零     将输出的前面补上0，直到占满指定列宽为止（不可以搭配使用“-”）

     
     转义字符 表
     \a    警报（响铃）符
     \b    回退符
     \f    换页符
     \n    换行符
     \r    回车符
     \t    横向制表符
     \v    纵向制表符
     \\    反斜杠
     \"    双引号
     %%    百分号符
     */
    
    printf("\n\nstringFormat\n");
    printf(" \
int i=30122121;\n \
long i2=309095024l;\n \
short i3=30;\n \
unsigned i4=2123453;\n \
    \n");

    int i=30122121;
    long i2=309095024l;
    short i3=30;
    unsigned i4=2123453;
    printf("%%d,%%o,%%x,%%X,%%ld,%%hd,%%u(i,i,i,i,i2,i3,i4) --> %d,%o,%x,%X,%ld,%hd,%u\n",i,i,i,i,i2,i3,i4);//如果是：%l,%h，则输不出结果
    printf("%%d,%%#o,%%#x,%%#X(i,i,i) --> %d,%#o,%#x,%#X\n",i,i,i,i);//如果是：%l,%h，则输不出结果
    //printf("%%hd,%%hd(i,i3) --> %hd,%hd\n\n\n",i,i3);//试验了%hd和%d之间的差别，因为short是2bytes
    
    //for string and char
    char ch1='d';
    unsigned char ch2=160;
    char *str="Hello everyone!";
    printf("%c,%u,%s\n\n\n",ch1,ch2,str);//unsigned char超过128的没有字符对应
 
    //for float and double,unsigned and signed can not be used with double and float
    float fl=2.566545445F;//or 2.566545445f
    double dl=265.5651445;
    long double dl2=2.5654441454;
    printf(" \
float fl=2.566545445F; \n \
double dl=265.5651445; \n \
long double dl2=2.5654441454; \n \
    \n");
    //%g没有e格式，默认6位包括小数点前面的数,
    //%f没有e格式，默认6位仅只小数点后面包含6位
    //%e采用e格式，默认6位为转化后的小数点后面的6位
    printf("%%f,%%e,%%g,%%.7f(fl,dl,dl,dl) --> %f,%e,%g,%.7f\n",fl,dl,dl,dl);
    printf("%%f,%%E,%%G,%%f(fl,dl,dl,dl) --> %f,%E,%G,%f\n",fl,dl,dl,dl);//%F is wrong
    printf("%%.8f,%%.10e(fl,dl) --> %.8f,%.10e\n",fl,dl);
    printf("%%.8e,%%.10f(fl,dl) --> %.8e,%.10f\n\n\n",fl,dl);
    
    //for point
    int *iP=&i;
    char *iP1= (char*)malloc(1);
    void *iP2;//dangerous!
    printf("%p,%p,%p\n\n\n",iP,iP1,iP2);
 
    //%后跟 数字表示宽度，负号表示左对齐（默认是右对齐), .跟上数字表示精度，如果格式化的是字符串，字符串可能被截断
    char *s="Hello world!(size=21)";
    printf("%s -- %%30s  --> :%30s:\n", s, s);
    printf("%s -- %%-30s --> :%-30s:\n", s, s);
    printf("%s -- %%30.10s  --> :%30.10s:\n", s, s);
    printf("%s -- %%-30.10s --> :%-30.10s:\n\n\n", s, s);
    
    double ddd=563.90855644;
    printf("%f -- %%30f  --> :%30f:\n", ddd, ddd);
    printf("%f -- %%-30f --> :%-30f:\n", ddd, ddd);
    printf("%f -- %%30.10f  --> :%30.10f:\n", ddd, ddd);
    printf("%f -- %%-30.10f --> :%-30.10f:\n\n\n", ddd, ddd);

    printf("%%5d(1000)  --> :%5d\n",1000);                      //默认右对齐,左边补空格
    printf("%%-5d(1000) --> :%-5d\n",1000);                     //左对齐,右边补空格
    printf("%%+d %%+d    --> :%+d %+d\n",1000,-1000);           //输出正负号
    printf("%% d %% d    --> :% d % d\n",1000,-1000);           //正号用空格替代，负号输出
    printf("%%05dd      --> :%05d\n",1000);                     //前面补0
    
    printf("%%.0f %%#.0f --> :%.0f %#.0f\n",1000.0,1000.0);     //当小数点后不输出值时依然输出小数点
    printf("%%g %%#g     --> :%g %#g\n",1000.0,1000.0);         //保留小数点后后的0
}

char *regexSearchFirst(char *ostr, char *regexStr) {
    
    //    标准的C和C++都不支持正则表达式，但有一些函数库可以辅助C/C++程序员完成这一功能，其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库，许多Linux发行版本都带有这个函数库。
    //    C语言处理正则表达式常用的函数有regcomp()、regexec()、regfree()和regerror()，一般分为三个步骤，如下所示：
    //    C语言中使用正则表达式一般分为三步：
    //    编译正则表达式 regcomp()
    //    匹配正则表达式 regexec()
    //    释放正则表达式 regfree()
    //    https://blog.csdn.net/weixin_26854475/article/details/117039709
    
    regex_t oregex;
    int nerrcode = 0;
    char szerrmsg[1024] = {0};
    size_t unerrmsglen = 0;
    regmatch_t regexMatchArr[1];
    
    if ((nerrcode = regcomp(&oregex, regexStr, REG_EXTENDED|REG_ENHANCED)) == 0) {
        
        // regexec 不能通过一次调用找到字符串中所有满足匹配条件的字符串位置,所以需要通过步进偏移的方式循环执行 regexec 才能把字符串中所有满足条件的匹配找出来
        // 如果只有 REG_EXTENDED , \b \d \w regexec 函数不认
        // 加上 REG_ENHANCED 后 , \b \d \w regexec 函数可以识别
        if ((nerrcode = regexec(&oregex, ostr, 1, regexMatchArr, 0)) == 0)    {
            
            size_t ostrLen = strlen(ostr);
            
            if(regexMatchArr[0].rm_eo < 0 || regexMatchArr[0].rm_so < 0 || regexMatchArr[0].rm_eo >= ostrLen || regexMatchArr[0].rm_so >= ostrLen) {
                regfree(&oregex);
                return NULL;
            }
            
            size_t matchLen = regexMatchArr[0].rm_eo - regexMatchArr[0].rm_so;
            if (matchLen > 0 && matchLen < ostrLen) {
                char *matchStr = (char *)malloc(matchLen+1);
                char *copyPtr = ostr;
                copyPtr += regexMatchArr[0].rm_so;
                strncpy(matchStr, copyPtr, matchLen);
                matchStr[matchLen] = 0;
                
                regfree(&oregex);
                return matchStr;
            }
        }
    }
    
    unerrmsglen = regerror(nerrcode, &oregex, szerrmsg, sizeof(szerrmsg));
    unerrmsglen = unerrmsglen < sizeof(szerrmsg) ? unerrmsglen : sizeof(szerrmsg) - 1;
    szerrmsg[unerrmsglen] = '\0';
    //printf("regexSearch, %s, ErrMsg: %s\n", regexStr, szerrmsg);
    regfree(&oregex);
    
    return NULL;
}

char **regexSearchAll(char *ostr, char *regexStr, int *matchNum) {
    char *str2Search = ostr;
    char *strMatch = NULL;
    *matchNum = 0;
    
    int maxMatchNum = 10;
    char ** rtnMatchArr = (char **)malloc(sizeof(char *) * maxMatchNum);
    memset(rtnMatchArr, maxMatchNum, sizeof(char *));
    
    while ((strMatch = regexSearchFirst(str2Search, regexStr)) != NULL) {
        if (0 == strMatch) {
            break;
        }
        
        rtnMatchArr[*matchNum] = strMatch;
        
        *matchNum = *matchNum + 1;
        if (*matchNum >= maxMatchNum) {
            break;
        }
        
        //printf("regexSearchAll, %s -> %s\n", regexStr, strMatch);
        
        // str2Search 移动到匹配 子字符串 的后面，做新的匹配
        char *matchPos = strstr(str2Search, strMatch);
        if (! matchPos) {
            break;
        }
        str2Search = matchPos;
        str2Search += strlen(strMatch);
        
        //printf("str2Search %s, regexStr %s\n", str2Search, regexStr);
    }
    
    return rtnMatchArr;
}

void checkAndFreeRegMatchArr(char **matchArr, int matchNum) {
    if (0 == matchNum || (!matchArr)) {
        return;
    }
    
    for (int i=0; i<matchNum; ++i) {
        printf("%d : %s\n", i, matchArr[i]);
    }
    for (int i=0; i<matchNum; ++i) {
        free(matchArr[i]);
    }
    free(matchArr);
}

void regexSearchLogic(void) {
    
    char *ostr = "百科熊猫词条  https://baike.baidu.com/item/%E5%A4%A7%E7%86%8A%E7%8C%AB/34935?fromtitle=%E7%86%8A%E7%8C%AB&fromid=162918&fr=aladdin  panda@163.com 大熊猫（学名：Ailuropoda melanoleuca）：属于食肉目、熊科、大熊猫亚科和大熊猫属唯一的哺乳动物，头躯长1.2-1.8米，尾长10-12厘米。体重80-120千克，最重可达180千克，体色为黑白两色19660000062，它有着圆圆的脸颊，大大的黑眼圈，胖嘟嘟的身体，标志性的内八字的行走方式，也有解剖刀般锋利的爪子。是世界上最可爱的动物之一。  panda@gmail.com  com.panda@gmail (010)88886666 ，或 022-22334455，或 02912345678 511122198812068932 41112219881206893X 18660000062 17000000026 ";
    
    //char *ostr = "https://baike.baidu.com/item/%E5%A4%A7%E7%86%8A%E7%8C%AB/34935?fromtitle=%E7%86%8A%E7%8C%AB&fromid=162918&fr=aladdin  panda@163.com  panda@gmail.com  com.panda@gmail (010)88886666 022-22334455 02912345678 511122198812068932 41112219881206893X 18660000062 17000000026 ";
    
    /*
     
     ^[A-Za-z]+$　　          //匹配由26个英文字母组成的字符串
     ^[A-Za-z0-9]+$　　       //匹配由数字和26个英文字母组成的字符串
     ^\w+$　　                //匹配由数字、26个英文字母或者下划线组成的字符串 相当于“[a-zA-Z0-9\u4E00-\u9FFF_]”
     [ ]                     //表示内容的范围； eg:“[a-zA-Z]”表示任一字母；“[ab]”表示“a”或“b”
     {}                      //表示数量的范围； eg:“{2}”表示2个；“{2,5}”表示2个到5个；“{3,}”表示至少3个；
     |                       //表示 或；eg:“a|b”表示“a”或“b”
     \num                    //num为一个整数，表示前面的表达式复制num次；eg:“(ab)\3”表示“ababab”
     \b                      //表示单词的开始或结束；
     \d                      //表示数字，相当于“[0-9]”;
     \s                      //表示任意空白符；
     
     反义字符
     \W                     //表示不是字符数字下划线或者汉字的字符；
     \S                     //表示任意不是空白符的字符；
     \D                     //表示不是数字的字符；
     \B                     //表示不是单词开头或结束的位置；
     [^x]                   //表示除“x”外的任意字符；
     [^abc]                 //表示除“a”“b”“c”外的任意字符；
    
     
     \\d                // 代表数字
     {2}                // 代表有两个
     {2,4}              // 代表有2到4个
     ?                  // 代表0或1个
     +                  // 代表至少1个
     *                  // 代表0个或多个
     ^                  // 代表以...开头
     $                  // 代表以...结束
     .                  // 代表除换行符以外的任意字符
     [\u4e00-\u9fa5]    // 匹配中文字符
     
     */
    
 
    // 身份证
    char *idPattern = "\\b([0-9]{17}|[0-9]{14})([0-9]|[xX])\\b";
    int matchNum = 0;
    char **regexSearch1 = regexSearchAll(ostr, idPattern, &matchNum);
    printf("\n身份证 正则\n");
    checkAndFreeRegMatchArr(regexSearch1, matchNum);
    
    // 电话号码：eg：021-68686868  0511-6868686；
    char *phonePattern = "\\b([0-9]{3,4}-)[0-9]{7,8}\\b";
    char **regexSearch2 = regexSearchAll(ostr, phonePattern, &matchNum);
    printf("\n电话号码 正则\n");
    checkAndFreeRegMatchArr(regexSearch2, matchNum);
    
    // 手机
    char *mobilePhonePattern = "\\b1[0-9]{10}\\b";
    char **regexSearch3 = regexSearchAll(ostr, mobilePhonePattern, &matchNum);
    printf("\n手机 正则\n");
    checkAndFreeRegMatchArr(regexSearch3, matchNum);
    
    // 邮箱
    char *emailPattern = "[a-zA-Z0-9][a-zA-Z0-9\u4E00-\u9FFF_]*@[a-zA-Z0-9]+\\.[a-zA-Z]{2,4}";
    char **regexSearch4 = regexSearchAll(ostr, emailPattern, &matchNum);
    printf("\n邮箱 正则\n");
    checkAndFreeRegMatchArr(regexSearch4, matchNum);
    
}

char* regexSearch2ReplaceAll(char *ostr, char *regexStr, char *replaceStr) {
    int matchNum = 0;

    char **regexMatchArr = regexSearchAll(ostr, regexStr, &matchNum);
    
    char *replaced = NULL;
    char *input = ostr;
    for (int i=0; i<matchNum; ++i) {
        printf("%d : %s\n", i, regexMatchArr[i]);
        replaced = replaceSubStr(input, regexMatchArr[i], replaceStr);
        if (input != ostr) {
            free(input);
        }
        input = replaced;
    }
    
    return replaced ? replaced: malloc2Strcpy(ostr);
}

void regexReplaceLogic(void) {
    
    printf("\nregexReplaceLogic\n");
    
    char *ostr = "百科熊猫词条  https://baike.baidu.com/item/%E5%A4%A7%E7%86%8A%E7%8C%AB/34935?fromtitle=%E7%86%8A%E7%8C%AB&fromid=162918&fr=aladdin  panda@163.com 大熊猫（学名：Ailuropoda melanoleuca）：属于食肉目、熊科、大熊猫亚科和大熊猫属唯一的哺乳动物，头躯长1.2-1.8米，尾长10-12厘米。体重80-120千克，最重可达180千克，体色为黑白两色19660000062，它有着圆圆的脸颊，大大的黑眼圈，胖嘟嘟的身体，标志性的内八字的行走方式，也有解剖刀般锋利的爪子。是世界上最可爱的动物之一。  panda@gmail.com  com.panda@gmail (010)88886666 ，或 022-22334455，或 02912345678 511122198812068932 41112219881206893X 18660000062 17000000026 ";
    
    // 手机
    char *replaceStr = "__cellPhoneNum__";
    char *mobilePhonePattern = "\\b1[0-9]{10}\\b";
    
    char* replacedStr = regexSearch2ReplaceAll(ostr, mobilePhonePattern, replaceStr);
    if (replacedStr) {
        printf("replacedStr : %s\n", replacedStr);
        free(replacedStr);
    } else {
        printf("regexSearch2ReplaceAll return NULL\n");
    }
    
}

void demoStringLogic(void) {
    logWsBegin("demoStringLogic begin\n");
    
    stringJoin();
    stringFromToArray();
    subStringSearch();
    stringTrim();
    stringFormat();
    regexSearchLogic();
    regexReplaceLogic();
    
    logWs("demoStringLogic end\n");
}
